// $codepro.audit.disable nullPointerDereference
/*
 * RequestAppointmentHandler.java
 * 
 * @version 1.0, March 6, 2011
 * CS 2340 Team 10: Cyberdyne Systems
 */
package edu.gatech.oad.cyberdoc.domain.action;

import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.json.JSONException;
import org.json.JSONObject;

import edu.gatech.oad.cyberdoc.database.DatabaseException;

import edu.gatech.oad.cyberdoc.domain.Appointment;
import edu.gatech.oad.cyberdoc.domain.AppointmentSpec;
import edu.gatech.oad.cyberdoc.domain.Doctor;
import edu.gatech.oad.cyberdoc.domain.Patient;
import edu.gatech.oad.cyberdoc.domain.TreatmentRecord;

import edu.gatech.oad.cyberdoc.domain.User;
import edu.gatech.oad.cyberdoc.server.Server;

/**
 * Handles request appointment actions by patients.
 * 
 * @author John Girata
 * @version 1.0, March 10, 2011
 */
public class SelectAppointmentHandler extends ActionHandler {
	/**
	 * The logger for this class.
	 */
	private static final Logger LOG;

	/**
	 * The action code this ActionHandler corresponds to.
	 */
	public static final String ACTION_CODE = "select_appt";

	static {
		LOG = Logger.getLogger("edu.gatech.oad.cyberdoc.domain."
				+ "SelectAppointmentHandler");
	}

	/**
	 * Registers this ActionHandler with the Patient class to start receiving
	 * actions.
	 */
	public SelectAppointmentHandler() {
		Patient.addActionHandler(ACTION_CODE, this);
	}

	/**
	 * Reverses everything done by the constructor.
	 */
	@Override
	public void destroy() {
		Patient.removeActionHandler(this);
	}

	/**
	 * Determines if this is a legal appointment and, if so, creates a new
	 * appointment.
	 * 
	 * This method assumes that the user executing this action is allowed to
	 * perform the action, and that this is the correct ActionHandler for
	 * handling this action.
	 * 
	 * This method expects the following fields:
	 * 
	 * datetime: The date and time as the number of milliseconds since the Unix
	 * epoch.
	 * 
	 * patient_username: The username of the patient requesting this appointment
	 * 
	 * doctor_username: The doctor requested for this appointment
	 * 
	 * reason: The reason this appointment is being requested
	 * 
	 * This method will return the following fields:
	 * 
	 * next: The next frame to show
	 * 
	 * @param source
	 *            The user that called this method
	 * @param command
	 *            The command encoding the action
	 * @return The result of performing this action
	 * @throws ActionHandleException
	 *             If a required field does not exist in the command, or if the
	 *             usernames given for the patient and doctor are invalid
	 */
	@Override
	public JSONObject handle(User source, JSONObject command)
			throws ActionHandleException {
		final JSONObject response = new JSONObject();

		try {
			final AppointmentSpec spec = buildSpec(command);

			if (Appointment.isConflicting(spec)) {
				// If conflicting, send the user back to the appointments page.
				// TODO Add details to why the user is going back
				response.put("next", "open_appts");

				// TODO Add more info about the appointment
				LOG.warning("User tried to add a bad appointment.");
			} else {
				// If it is open, create a new appointment and send them to the
				// options page.
				// FIXME Use actual ID numbers
				final int time = (int) (System.currentTimeMillis() % Integer.MAX_VALUE);
				final Appointment appt = new Appointment(time, spec);

				try {
					Server.getDatabase().save(appt);
				} catch (DatabaseException e) {
					LOG.log(Level.SEVERE,
							"Could not synchronize with the database", e);
				}

				response.put("next", "options");

				// TODO Add more info about the appointment
				LOG.info("Creating new appointment [id=" + time + "]");
			}
		} catch (JSONException e) {
			LOG.log(Level.SEVERE, "Unexpected exception while writing to "
					+ "response object.", e);
		}

		return response;
	}

	/**
	 * Converts the raw information from the command object into an
	 * AppointmentSpec instance.
	 * 
	 * @param command
	 *            The command to break down
	 * @return An AppointmentSpec instance corresponding to the command
	 * @throws ActionHandleException
	 *             If
	 * @throws ClassCastException
	 * @throws JSONException
	 */
	private AppointmentSpec buildSpec(JSONObject command)
			throws ActionHandleException {
		try {
			final long datetime = Long.parseLong(command.getString("datetime"));
			final String patientUsername = command
					.getString("patient_username");
			final String doctorUsername = command.getString("doctor_username");
			final String reason = command.getString("reason");

			final Date date = new Date(datetime);
			final Patient patient = (Patient) User.getUser(patientUsername);
			final Doctor doctor = (Doctor) User.getUser(doctorUsername);
			final TreatmentRecord treatmentRecord = new TreatmentRecord();

			if (patient == null || doctor == null) {
				throw new ActionHandleException(
						"Incorrect usernames for patient and doctor.");
			}

			return new AppointmentSpec(date, patient, doctor, reason, treatmentRecord);

		} catch (JSONException e) {
			throw new ActionHandleException("A required field does not exist.");
		} catch (ClassCastException e) {
			throw new ActionHandleException(
					"Incorrect usernames for patient and doctor.");
		} catch (NumberFormatException e) {
			throw new ActionHandleException("Invalid date format.");
		}
	}

	/**
	 * Returns a string representation of this class.
	 * 
	 * @return A string representation of this class.
	 */
	@Override
	public String toString() {
		return "SelectAppointmentHandler (action=\"" + ACTION_CODE + "\")";
	}
}
